This patch adds a new domain config option, 'cpus' which is a list of
authoremellor@leeni.uk.xensource.com <emellor@leeni.uk.xensource.com>
Fri, 2 Dec 2005 15:52:47 +0000 (15:52 +0000)
committeremellor@leeni.uk.xensource.com <emellor@leeni.uk.xensource.com>
Fri, 2 Dec 2005 15:52:47 +0000 (15:52 +0000)
CPUs a domains' vcpus can use.  The older 'cpu' config option is
prepended to the list of cpus to use and will keep the behavior of
pinning VCPU0.

The cpus option supports ranges and negation, so:

cpus = "0-3,5,^1" produces -> [0,2,3,5]

The list is circular, so in a domain with the following config:

vcpus = 4
cpus  = "0,3,7"  # Use any of 0, 3, 7 for this domain.

would see vcpus 0-3 pinned to cpus 0,3,7,0 respectively.

Also, the pin operation is moved before the memory reservation as vcpu
to cpu mapping will be helpful for future NUMA work when trying to
allocate pages close to the physical cpus being used.

An update to the display of cpumap was needed to normalize the cpumap
values to the range of possible cpus.

I've also included some text for the xmdomain.cfg(5) man page.

Signed-off-by: Ryan Harper <ryanh@us.ibm.com>
docs/man/xmdomain.cfg.pod.5
tools/examples/xmexample.vmx
tools/examples/xmexample.vti
tools/examples/xmexample1
tools/examples/xmexample2
tools/examples/xmexample3
tools/python/xen/xend/XendDomainInfo.py
tools/python/xen/xm/create.py
tools/python/xen/xm/main.py

index 4b9a35a446f56cebe5e4fe9e535f663310bd9b4f..df868543b1c8b98f410702457cb4247c0fdc2b9d 100644 (file)
@@ -156,6 +156,16 @@ Specifies which CPU the domain should be started on, where 0 specifies
 the first cpu, 1 the second, and so on.  This defaults to -1, which
 means Xen is free to pick which CPU to start on.
 
+=item B<cpus>
+
+Specifies a list of CPUs on which the domains' VCPUs are allowed to
+execute upon.  The syntax supports ranges (0-3), and negation, ^1.
+For instance:
+
+    cpus = "0-3,5,^1"
+
+Will result in CPUs 0, 2, 3, 5 being available for use by the domain.
+
 =item B<extra>
 
 Extra information to append to the end of the kernel parameter line.
index e3d4d44ba97c735a23473e86c6382f338de2ea4d..8b694e32feabc5af77a3e082ce3eb736c5efaa23 100644 (file)
@@ -30,8 +30,10 @@ name = "ExampleVMXDomain"
 # the number of cpus guest platform has, default=1
 vcpus=1
 
-# Which CPU to start domain on? 
-#cpu = -1   # leave to Xen to pick
+# List of which CPUS this domain is allowed to use, default Xen picks
+#cpus = ""         # leave to Xen to pick
+#cpus = "0"        # all vcpus run on CPU0
+#cpus = "0-3,5,^1" # run on cpus 0,2,3,5
 
 # Optionally define mac and/or bridge for the network interfaces.
 # Random MACs are assigned if not given.
index 540543a991f988b5ec85f01c5a2f769028f12027..5722eb0699f92f9fa0a0aadcc29987061671243e 100644 (file)
@@ -23,8 +23,10 @@ memory = 256
 # A name for your domain. All domains must have different names.
 name = "ExampleVMXDomain"
 
-# Which CPU to start domain on? 
-#cpu = -1   # leave to Xen to pick
+# List of which CPUS this domain is allowed to use, default Xen picks
+#cpus = ""         # leave to Xen to pick
+#cpus = "0"        # all vcpus run on CPU0
+#cpus = "0-3,5,^1" # run on cpus 0,2,3,5
 
 # Disable vif for now
 nics=0
index cd73c7c3fafc0e2dc972d44dbd928a45d8505569..4e7fb4d72f6d4f6d06dced28118ce8b3266a492a 100644 (file)
@@ -22,8 +22,10 @@ memory = 64
 # A name for your domain. All domains must have different names.
 name = "ExampleDomain"
 
-# Which CPU to start domain on? 
-#cpu = -1   # leave to Xen to pick
+# List of which CPUS this domain is allowed to use, default Xen picks
+#cpus = ""         # leave to Xen to pick
+#cpus = "0"        # all vcpus run on CPU0
+#cpus = "0-3,5,^1" # run on cpus 0,2,3,5
 
 # Number of Virtual CPUS to use, default is 1
 #vcpus = 1
index ba89eef7eb1fbd9626be9220e8c1464585c246f8..6a562a96094ebce1a1fc3c1f133aa6d65ce9f544 100644 (file)
@@ -51,9 +51,11 @@ memory = 64
 # so we use the vmid to create a name.
 name = "VM%d" % vmid
 
-# Which CPU to start domain on? 
-#cpu = -1   # leave to Xen to pick
-cpu = vmid  # set based on vmid (mod number of CPUs)
+# List of which CPUS this domain is allowed to use, default Xen picks
+#cpus = ""         # leave to Xen to pick
+#cpus = "0"        # all vcpus run on CPU0
+#cpus = "0-3,5,^1" # run on cpus 0,2,3,5
+#cpus = "%s" % vmid # set based on vmid (mod number of CPUs)
 
 # Number of Virtual CPUS to use, default is 1
 #vcpus = 1
index 45211c71296f53021b97a0997de2012c2bd90a98..cdb993a2ff936ce50a504214894f219fa34fb514 100644 (file)
@@ -51,9 +51,11 @@ memory = 64
 # so we use the vmid to create a name.
 name = "VM%d" % vmid
 
-# Which CPU to start domain on? 
-#cpu = -1   # leave to Xen to pick
-cpu = vmid  # set based on vmid (mod number of CPUs)
+# List of which CPUS this domain is allowed to use, default Xen picks
+#cpus = ""         # leave to Xen to pick
+#cpus = "0"        # all vcpus run on CPU0
+#cpus = "0-3,5,^1" # run on cpus 0,2,3,5
+cpus = "%s" % vmid # set based on vmid (mod number of CPUs)
 
 #----------------------------------------------------------------------------
 # Define network interfaces.
index 0a2ebe52aaa115cc5a17ade7d30e58d7192825b3..9c9d23b436724443d3223d8d6a31148590cacc36 100644 (file)
@@ -286,6 +286,7 @@ def parseConfig(config):
         result[e[0]] = get_cfg(e[0], e[1])
 
     result['cpu']       = get_cfg('cpu',       int)
+    result['cpus']      = get_cfg('cpus',      str)
     result['image']     = get_cfg('image')
 
     try:
@@ -299,6 +300,43 @@ def parseConfig(config):
             'Invalid configuration setting: vcpus = %s: %s' %
             (sxp.child_value(result['image'], 'vcpus', 1), str(exn)))
 
+    try:
+        # support legacy config files with 'cpu' parameter
+        # NB: prepending to list to support previous behavior
+        #     where 'cpu' parameter pinned VCPU0.
+        if result['cpu']:
+           if result['cpus']:
+               result['cpus'] = "%s,%s" % (str(result['cpu']), result['cpus'])
+           else:
+               result['cpus'] = str(result['cpu'])
+
+        # convert 'cpus' string to list of ints
+        # 'cpus' supports a list of ranges (0-3), seperated by
+        # commas, and negation, (^1).  
+        # Precedence is settled by  order of the string:
+        #     "0-3,^1"   -> [0,2,3]
+        #     "0-3,^1,1" -> [0,1,2,3]
+        if result['cpus']:
+            cpus = []
+            for c in result['cpus'].split(','):
+                if c.find('-') != -1:             
+                    (x,y) = c.split('-')
+                    for i in range(int(x),int(y)+1):
+                        cpus.append(int(i))
+                else:
+                    # remove this element from the list 
+                    if c[0] == '^':
+                        cpus = [x for x in cpus if x != int(c[1])]
+                    else:
+                        cpus.append(int(c))
+
+            result['cpus'] = cpus
+        
+    except ValueError, exn:
+        raise VmError(
+            'Invalid configuration setting: cpus = %s: %s' %
+            (result['cpus'], exn))
+
     result['backend'] = []
     for c in sxp.children(config, 'backend'):
         result['backend'].append(sxp.name(sxp.child0(c)))
@@ -486,6 +524,7 @@ class XendDomainInfo:
             defaultInfo('on_reboot',    lambda: "restart")
             defaultInfo('on_crash',     lambda: "restart")
             defaultInfo('cpu',          lambda: None)
+            defaultInfo('cpus',         lambda: [])
             defaultInfo('cpu_weight',   lambda: 1.0)
 
             # some domains don't have a config file (e.g. dom0 )
@@ -1130,9 +1169,15 @@ class XendDomainInfo:
             xc.domain_setmaxmem(self.domid, m)
             xc.domain_memory_increase_reservation(self.domid, m, 0, 0)
 
-            cpu = self.info['cpu']
-            if cpu is not None and cpu != -1:
-                xc.domain_pincpu(self.domid, 0, 1 << cpu)
+            # repin domain vcpus if a restricted cpus list is provided
+            # this is done prior to memory allocation to aide in memory
+            # distribution for NUMA systems.
+            cpus = self.info['cpus']
+            if cpus is not None and len(cpus) > 0:
+                for v in range(0, self.info['max_vcpu_id']+1):
+                    # pincpu takes a list of ints
+                    cpu = [ int( cpus[v % len(cpus)] ) ]
+                    xc.domain_pincpu(self.domid, v, cpu)
 
             self.createChannels()
 
index 211474dc7ecd65bad7796ab5b72b07394d88bb4c..7577d2b6374d1a6ad9e80fbc937bda817db2d997 100644 (file)
@@ -154,7 +154,11 @@ gopts.var('maxmem', val='MEMORY',
 
 gopts.var('cpu', val='CPU',
           fn=set_int, default=None,
-          use="CPU to run the domain on.")
+          use="CPU to run the VCPU0 on.")
+
+gopts.var('cpus', val='CPUS',
+          fn=set_int, default=None,
+          use="CPUS to run the domain on.")
 
 gopts.var('lapic', val='LAPIC',
           fn=set_int, default=0,
@@ -572,6 +576,8 @@ def make_config(vals):
     
     if vals.cpu is not None:
         config.append(['cpu', vals.cpu])
+    if vals.cpus is not None:
+        config.append(['cpus', vals.cpus])
     if vals.cpu_weight is not None:
         config.append(['cpu_weight', vals.cpu_weight])
     if vals.blkif:
index 83c1bc47944c80ce5e1a15862a2fb5f83a26ad5c..0053ac9aad44f1483324ad8931d3617fdc4cbfd1 100644 (file)
@@ -459,7 +459,9 @@ def xm_vcpu_list(args):
             for x in server.xend_node()[1:]:
                 if len(x) > 1 and x[0] == 'nr_cpus':
                     nr_cpus = int(x[1])
-                    cpumap = filter(lambda x: x < nr_cpus, cpumap)
+                    # normalize cpumap by modulus nr_cpus, and drop duplicates
+                    cpumap = dict.fromkeys(
+                                map(lambda x: x % nr_cpus, cpumap)).keys()
                     if len(cpumap) == nr_cpus:
                         return "any cpu"
                     break